#pragma once

#include <fstream>
#include <stdio.h>
#include <string>
using std::string;
#include <boost/archive/text_iarchive.hpp>
using boost::archive::text_iarchive;
#include <boost/archive/text_oarchive.hpp>
using boost::archive::text_oarchive;
#include "boost/filesystem/path.hpp"
#include "boost/filesystem.hpp"

class GamePlayer; // forward declaration
class GrudgeEvaluator; // forward declaration

/*
	Manages all storage and retrieval of system settings in some external medium.
	Each child of GameConfig handles the records of exactly one class in the GrudgeAI project.
	The GameConfig child is a C++ 'friend' of its paired class so that it may access
	the recorded attributes, without the paired class being involved in how the records
	are maintained.
*/
class GameConfig
{
public:
	/*
		Deserialization of this object.
		Access point for all GameConfigs.  Preps a stream to a file destination
		then hands over responsibility to a child class' loadDetails.
	*/
	void		load()
				{
					std::ifstream ifs(filePath);
					text_iarchive ar(ifs);
					loadDetails(ar);
				} // end method

	/*
		Companion to load(), which is overridden in children.
		Each child is responsible to the particular attributes that only it knows about.
	*/
	virtual
	void		loadDetails(text_iarchive&) = 0;

	/*
		Serialization of this object.
		Access point for all GameConfigs.  Preps a stream to a file source
		then hands over responsibility to a child class' saveDetails.
	*/
	void		save()
				{
					std::ofstream ofs(filePath);
					text_oarchive ar(ofs);
					saveDetails(ar);
				} // end method

	/*
		Companion to save(), which is overridden in children.
		Each child is responsible to the particular attributes that only it knows about.
	*/
	virtual
	void		saveDetails(text_oarchive&) = 0;

protected:
	// Source or destination for serialization.
	string		filePath;
}; // end class

/*
	Settings manager for the GameEngine.
	Meant to be considered the "highest level of configuration",
	above all individual object records.
*/
class GameMasterConfig :
	public GameConfig
{
public:
	/*
		Each GamePlayer needs to be identified somehow from its moemnt of genesis.
		System Unique IDs are assigned here, and they are as simple as they come.
		The first ID assigned is 0, and it counts up from there,
		never reassigning numbers, even in the unlikely case that the players
		they represent cease to exist.

		@return a non-negative number, never meant to be repeated for the lifetime of the project
	*/
	static
	unsigned
	int			obtainUniqueID();

protected:
	// This is the value that is being distributed and incremented by obtainUniqueID().
	static
	unsigned
	int					nextPlayerID;

	// Singleton reference to GameMasterConfig.
	static
	GameMasterConfig*	MASTER;

	/*
		Default constructor.
		The filepath is set to a specific top-level configuration file,
		which is anticipated never to change names.
		nextPlayerID is set briefly to 0 before any prior record overwrites it.
	*/
				GameMasterConfig()
				{
					filePath = "./.settings";
					nextPlayerID = 0;
					MASTER = this;

					try
					{
						MASTER->load();
					} catch (...)
					{
					} // end try
				} // end constructor

	/*
		Child-specific storage operation, responsible for top-level settings.
	*/
	virtual
	void		loadDetails(text_iarchive& ar);

	/*
		Child-specific retrieval operation, responsible for top-level settings.
	*/
	virtual
	void		saveDetails(text_oarchive& ar);
}; // end class

/*
	All GamePlayers are recorded via this GameConfig.
	There shouldn't be a need to create more than one of these objects
	since the number of occasions over which players are loaded and saved
	should be rare.
*/
class GamePlayerConfig :
	public GameConfig
{
public:
	/*
		Singleton accessor.  Exactly one object shoulders the workload
		of GamePlayer identity maintenance.
		@param p	GamePlayer being loaded or saved
		@return		GamePlayerConfig willing to do the job!
	*/
	static
	GamePlayerConfig&	of(GamePlayer* p);

	/*
		Translates an Account Name into a record location (filepath).
		In so doing, it ensures that the account-holding folder exists.

		@param accountName	mutable account name string (no validation)
							The parameter must be permitted to change
							so that its memory space can be retained
							in the return value.
		@return				accountName, with path prepended
							and suffix postpended (if applicable)
	*/
	static
	void		buildAccountLocation(string& accountName);

	/*
		Changes the primary key (filename) of an account from its current
		value to a different value.
		@param newName	the rename-to value
		@return			Windows/C++-provided success or fail code
	*/
	int			rename(string& newName);

protected:
	// Singleton reference that is accessed by of().
	static
	GamePlayerConfig*	PLAYER;
	// The GamePlayer that holds the focus of the config's current operations.
	static
	GamePlayer*			player;

	/*
		Default constructor.
		Doesn't include any initialization, since of() does that.
		It's declared here to prevent other programmers from instantiating
		a GamePlayerConfig distinct from the one under system control.
	*/
				GamePlayerConfig() {}

	/*
		Child-specific retrieval operation, responsible for per-player settings.
	*/
	virtual
	void		loadDetails(text_iarchive& ar);

	/*
		Child-specific storage operation, responsible for per-player settings.
	*/
	virtual
	void		saveDetails(text_oarchive& ar);
}; // end class

/*
	Records Grudge Values that are being provided by GrudgeEvaluator.
	Unlike the other GameConfigs, this one has not been implemented as a Singleton.
	It is expected that several AIs will be active at the same time,
	so giving each its own record manager eases future considerations
	such as multi-threading.  This does place a requirement on the AI, however:
	De-allocate the GrudgeConfig you've been given once you're done with it.

	Another unusual detail about this variety of config is that it is responsible
	to the GrudgeEvaluator, but the GrudgeEvaluator holds many individual player records.
	In order to retain the one-object-recorded-at-a-time model that I designed,
	it became necessary for the GrudgeEvaluator to set the player that is being recorded,
	via the forRecord() method.  Meanwhile, GrudgeEvaluator temporarily compromises
	its relationship with its data table, so that GrudgeConfig can refer to one
	GrudgeDataRow at a known location.  That location is: ai->records[ai->recordCount],
	where ai is a pointer to the GrudgeEvaluator.
*/
class GrudgeConfig :
	public GameConfig
{
public:
	/*
		Adjust the internal state of the GameConfig so it is
		ready to manage records belonging to %p%.
		@param p	a reference to a GamePlayer that is supposedly involved in a game right now
		@return		GameConfig already obtained via of(), reconfigured to handle the specified record
	*/
	GrudgeConfig*	forRecord(GamePlayer* p);

	/*
		Factory method.
		Each AI calls this method at least once to manage the AI's individual records.
		@param ai	who is asking?
		@return		GrudgeConfig constructed specifically for this AI's records
	*/
	static
	GrudgeConfig*	of(GrudgeEvaluator* ai);

protected:
	// The AI that is matched one-to-one with this configuration object.
	GrudgeEvaluator*	ai;
	// A root directory for all of the AI's records.
	// of() is responsible for initializing this value.  It is never changed afterwards.
	string				aiFolder;

	/*
		Child-specific retrieval operation, responsible for per-AI, player records.
	*/
	virtual
	void		loadDetails(text_iarchive& ar);

	/*
		Child-specific storage operation, responsible for per-AI, player records.
	*/
	virtual
	void		saveDetails(text_oarchive& ar);
}; // end class